{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# PMOD Grove OLED\n",
    "\n",
    "This notebook demonstrates writing two different grove OLED device drivers - one written completely in Python and another optimized driver using a Microblaze processor to handle the IIC protocol.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### References\n",
    "#### Beaglebone Python\n",
    "https://www.seeedstudio.com/Grove-OLED-Display-0.96%26quot%3B-p-781.html <br>\n",
    "https://github.com/adafruit/Adafruit_Python_GPIO/blob/master/Adafruit_GPIO/I2C.py <br>\n",
    "\n",
    "#### Arduino C++\n",
    "https://github.com/Seeed-Studio/OLED_Display_128X64/archive/master.zip <br>\n",
    "\n",
    "#### Xilinx IIC Driver\n",
    "http://www.xilinx.com/support/documentation/ip_documentation/axi_iic_ds756.pdf <br>\n",
    "https://github.com/analogdevicesinc/no-OS/blob/0629c39fac9aad8e236b648b28acccc3abb178dc/fmcomms1/Common/i2c_axi.c <br>\n",
    "<br>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "# Load in the base Overlay\n",
    "from pynq.overlays.base import BaseOverlay\n",
    "base = BaseOverlay(\"base.bit\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Python Grove OLED class\n",
    "This class is a demonstration of extending the PMODIIC class for fast development. For better performance, users should use the Microblaze C-based drivers (e.g. grove_oled.bin).\n",
    "\n",
    "References:\n",
    "https://www.seeedstudio.com/Grove-OLED-Display-0.96%26quot%3B-p-781.html"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from pynq.lib.pmod import PMOD_GROVE_G3\n",
    "from pynq.lib.pmod import PMOD_GROVE_G4\n",
    "from pynq.lib import Pmod_IIC\n",
    "\n",
    "basic_font = \\\n",
    "[[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x00,0x5F,0x00,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x00,0x07,0x00,0x07,0x00,0x00,0x00],\n",
    "[0x00,0x14,0x7F,0x14,0x7F,0x14,0x00,0x00],\n",
    "[0x00,0x24,0x2A,0x7F,0x2A,0x12,0x00,0x00],\n",
    "[0x00,0x23,0x13,0x08,0x64,0x62,0x00,0x00],\n",
    "[0x00,0x36,0x49,0x55,0x22,0x50,0x00,0x00],\n",
    "[0x00,0x00,0x05,0x03,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x1C,0x22,0x41,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x41,0x22,0x1C,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x08,0x2A,0x1C,0x2A,0x08,0x00,0x00],\n",
    "[0x00,0x08,0x08,0x3E,0x08,0x08,0x00,0x00],\n",
    "[0x00,0xA0,0x60,0x00,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x08,0x08,0x08,0x08,0x08,0x00,0x00],\n",
    "[0x00,0x60,0x60,0x00,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x20,0x10,0x08,0x04,0x02,0x00,0x00],\n",
    "[0x00,0x3E,0x51,0x49,0x45,0x3E,0x00,0x00],\n",
    "[0x00,0x00,0x42,0x7F,0x40,0x00,0x00,0x00],\n",
    "[0x00,0x62,0x51,0x49,0x49,0x46,0x00,0x00],\n",
    "[0x00,0x22,0x41,0x49,0x49,0x36,0x00,0x00],\n",
    "[0x00,0x18,0x14,0x12,0x7F,0x10,0x00,0x00],\n",
    "[0x00,0x27,0x45,0x45,0x45,0x39,0x00,0x00],\n",
    "[0x00,0x3C,0x4A,0x49,0x49,0x30,0x00,0x00],\n",
    "[0x00,0x01,0x71,0x09,0x05,0x03,0x00,0x00],\n",
    "[0x00,0x36,0x49,0x49,0x49,0x36,0x00,0x00],\n",
    "[0x00,0x06,0x49,0x49,0x29,0x1E,0x00,0x00],\n",
    "[0x00,0x00,0x36,0x36,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x00,0xAC,0x6C,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x08,0x14,0x22,0x41,0x00,0x00,0x00],\n",
    "[0x00,0x14,0x14,0x14,0x14,0x14,0x00,0x00],\n",
    "[0x00,0x41,0x22,0x14,0x08,0x00,0x00,0x00],\n",
    "[0x00,0x02,0x01,0x51,0x09,0x06,0x00,0x00],\n",
    "[0x00,0x32,0x49,0x79,0x41,0x3E,0x00,0x00],\n",
    "[0x00,0x7E,0x09,0x09,0x09,0x7E,0x00,0x00],\n",
    "[0x00,0x7F,0x49,0x49,0x49,0x36,0x00,0x00],\n",
    "[0x00,0x3E,0x41,0x41,0x41,0x22,0x00,0x00],\n",
    "[0x00,0x7F,0x41,0x41,0x22,0x1C,0x00,0x00],\n",
    "[0x00,0x7F,0x49,0x49,0x49,0x41,0x00,0x00],\n",
    "[0x00,0x7F,0x09,0x09,0x09,0x01,0x00,0x00],\n",
    "[0x00,0x3E,0x41,0x41,0x51,0x72,0x00,0x00],\n",
    "[0x00,0x7F,0x08,0x08,0x08,0x7F,0x00,0x00],\n",
    "[0x00,0x41,0x7F,0x41,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x20,0x40,0x41,0x3F,0x01,0x00,0x00],\n",
    "[0x00,0x7F,0x08,0x14,0x22,0x41,0x00,0x00],\n",
    "[0x00,0x7F,0x40,0x40,0x40,0x40,0x00,0x00],\n",
    "[0x00,0x7F,0x02,0x0C,0x02,0x7F,0x00,0x00],\n",
    "[0x00,0x7F,0x04,0x08,0x10,0x7F,0x00,0x00],\n",
    "[0x00,0x3E,0x41,0x41,0x41,0x3E,0x00,0x00],\n",
    "[0x00,0x7F,0x09,0x09,0x09,0x06,0x00,0x00],\n",
    "[0x00,0x3E,0x41,0x51,0x21,0x5E,0x00,0x00],\n",
    "[0x00,0x7F,0x09,0x19,0x29,0x46,0x00,0x00],\n",
    "[0x00,0x26,0x49,0x49,0x49,0x32,0x00,0x00],\n",
    "[0x00,0x01,0x01,0x7F,0x01,0x01,0x00,0x00],\n",
    "[0x00,0x3F,0x40,0x40,0x40,0x3F,0x00,0x00],\n",
    "[0x00,0x1F,0x20,0x40,0x20,0x1F,0x00,0x00],\n",
    "[0x00,0x3F,0x40,0x38,0x40,0x3F,0x00,0x00],\n",
    "[0x00,0x63,0x14,0x08,0x14,0x63,0x00,0x00],\n",
    "[0x00,0x03,0x04,0x78,0x04,0x03,0x00,0x00],\n",
    "[0x00,0x61,0x51,0x49,0x45,0x43,0x00,0x00],\n",
    "[0x00,0x7F,0x41,0x41,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x02,0x04,0x08,0x10,0x20,0x00,0x00],\n",
    "[0x00,0x41,0x41,0x7F,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x04,0x02,0x01,0x02,0x04,0x00,0x00],\n",
    "[0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00],\n",
    "[0x00,0x01,0x02,0x04,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x20,0x54,0x54,0x54,0x78,0x00,0x00],\n",
    "[0x00,0x7F,0x48,0x44,0x44,0x38,0x00,0x00],\n",
    "[0x00,0x38,0x44,0x44,0x28,0x00,0x00,0x00],\n",
    "[0x00,0x38,0x44,0x44,0x48,0x7F,0x00,0x00],\n",
    "[0x00,0x38,0x54,0x54,0x54,0x18,0x00,0x00],\n",
    "[0x00,0x08,0x7E,0x09,0x02,0x00,0x00,0x00],\n",
    "[0x00,0x18,0xA4,0xA4,0xA4,0x7C,0x00,0x00],\n",
    "[0x00,0x7F,0x08,0x04,0x04,0x78,0x00,0x00],\n",
    "[0x00,0x00,0x7D,0x00,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x80,0x84,0x7D,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x7F,0x10,0x28,0x44,0x00,0x00,0x00],\n",
    "[0x00,0x41,0x7F,0x40,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x7C,0x04,0x18,0x04,0x78,0x00,0x00],\n",
    "[0x00,0x7C,0x08,0x04,0x7C,0x00,0x00,0x00],\n",
    "[0x00,0x38,0x44,0x44,0x38,0x00,0x00,0x00],\n",
    "[0x00,0xFC,0x24,0x24,0x18,0x00,0x00,0x00],\n",
    "[0x00,0x18,0x24,0x24,0xFC,0x00,0x00,0x00],\n",
    "[0x00,0x00,0x7C,0x08,0x04,0x00,0x00,0x00],\n",
    "[0x00,0x48,0x54,0x54,0x24,0x00,0x00,0x00],\n",
    "[0x00,0x04,0x7F,0x44,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x3C,0x40,0x40,0x7C,0x00,0x00,0x00],\n",
    "[0x00,0x1C,0x20,0x40,0x20,0x1C,0x00,0x00],\n",
    "[0x00,0x3C,0x40,0x30,0x40,0x3C,0x00,0x00],\n",
    "[0x00,0x44,0x28,0x10,0x28,0x44,0x00,0x00],\n",
    "[0x00,0x1C,0xA0,0xA0,0x7C,0x00,0x00,0x00],\n",
    "[0x00,0x44,0x64,0x54,0x4C,0x44,0x00,0x00],\n",
    "[0x00,0x08,0x36,0x41,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x00,0x7F,0x00,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x41,0x36,0x08,0x00,0x00,0x00,0x00],\n",
    "[0x00,0x02,0x01,0x01,0x02,0x01,0x00,0x00],\n",
    "[0x00,0x02,0x05,0x05,0x02,0x00,0x00,0x00]]\n",
    "\n",
    "class Python_Grove_OLED(Pmod_IIC):\n",
    "    \"\"\"This class controls the Grove OLED.\n",
    "    \n",
    "    This class inherits from the PMODIIC class.\n",
    "    \n",
    "    Attributes\n",
    "    ----------\n",
    "    iop : _IOP\n",
    "        The _IOP object returned from the DevMode.\n",
    "    scl_pin : int\n",
    "        The SCL pin number.\n",
    "    sda_pin : int\n",
    "        The SDA pin number.\n",
    "    iic_addr : int\n",
    "        The IIC device address.\n",
    "    \n",
    "    \"\"\"\n",
    "    def __init__(self, pmod_id, gr_pins): \n",
    "        \"\"\"Return a new instance of a grove OLED object. \n",
    "    \n",
    "        Note\n",
    "        ----\n",
    "        \n",
    "        Parameters\n",
    "        ----------\n",
    "        pmod_id : int\n",
    "            The PMOD ID (1, 2) corresponding to (PMODA, PMODB).\n",
    "        gr_pins: list\n",
    "            Adapter pins selected.\n",
    "            \n",
    "        \"\"\"\n",
    "        if gr_pins in [PMOD_GROVE_G3,PMOD_GROVE_G4]:\n",
    "            [scl_pin,sda_pin] = gr_pins\n",
    "        else:\n",
    "            raise ValueError(\"Valid Grove Pins are on G3 or G4.\")\n",
    "        \n",
    "        super().__init__(pmod_id, scl_pin, sda_pin, 0x3C)\n",
    "        \n",
    "        # Unlock OLED driver IC MCU interface\n",
    "        self._send_cmd(0xFD) \n",
    "        self._send_cmd(0x12)\n",
    "        # Set display off\n",
    "        self._send_cmd(0xAE)\n",
    "        # Switch on display\n",
    "        self._send_cmd(0xAF) \n",
    "        self._send_cmd(0xA4)\n",
    "        \n",
    "    def _send_cmd(self, word):\n",
    "        \"\"\"Send a command to the IIC driver.\n",
    "        \n",
    "        This method relies on the send() in the parent class.\n",
    "        \n",
    "        Parameters\n",
    "        ----------\n",
    "        word : int\n",
    "            A 32-bit command word to be written to the driver.\n",
    "            \n",
    "        Returns\n",
    "        -------\n",
    "        None\n",
    "        \n",
    "        \"\"\"\n",
    "        self.send([0x80,word])\n",
    "        \n",
    "    def _send_data(self, word):\n",
    "        \"\"\"Send a command to the IIC driver.\n",
    "        \n",
    "        This method relies on the send() in the parent class.\n",
    "        \n",
    "        Parameters\n",
    "        ----------\n",
    "        word : int\n",
    "            A 32-bit data word to be written to the driver.\n",
    "            \n",
    "        Returns\n",
    "        -------\n",
    "        None\n",
    "        \n",
    "        \"\"\"\n",
    "        self.send([0x40,word])\n",
    "    \n",
    "    def set_normal_mode(self):\n",
    "        \"\"\"Set the display mode to 'normal'.\n",
    "        \n",
    "        Parameters\n",
    "        ----------\n",
    "        None\n",
    "        \n",
    "        Returns\n",
    "        -------\n",
    "        None\n",
    "        \n",
    "        \"\"\"\n",
    "        self._send_cmd(0xA4)\n",
    "    \n",
    "    def set_inverse_mode(self):\n",
    "        \"\"\"Set the display mode to 'inverse'.\n",
    "        \n",
    "        This mode has white background and black characters.\n",
    "        \n",
    "        Parameters\n",
    "        ----------\n",
    "        None\n",
    "        \n",
    "        Returns\n",
    "        -------\n",
    "        None\n",
    "        \n",
    "        \"\"\"\n",
    "        self._send_cmd(0xA7)\n",
    "    \n",
    "    def _put_char(self, chr):\n",
    "        \"\"\"Print a single character on the OLED screen.\n",
    "        \n",
    "        Note\n",
    "        ----\n",
    "        This method is only for internal use of this class. To print strings\n",
    "        or characters, users should use the write() method.\n",
    "        \n",
    "        Parameters\n",
    "        ----------\n",
    "        chr : str\n",
    "            A string of length 1 to be put onto the screen.\n",
    "            \n",
    "        Returns\n",
    "        -------\n",
    "        None\n",
    "        \n",
    "        \"\"\"\n",
    "        global basic_font\n",
    "        c_add=ord(chr)\n",
    "        if c_add<32 or c_add>127:     \n",
    "            # Ignore non-printable ASCII characters\n",
    "            chr = ' '\n",
    "            c_add=ord(chr)\n",
    "        for j in range(8):\n",
    "            self._send_data(basic_font[c_add-32][j])\n",
    "\n",
    "    def set_XY(self, row, column):\n",
    "        \"\"\"Set the location where to start printing.\n",
    "        \n",
    "        Parameters\n",
    "        ----------\n",
    "        row : int\n",
    "            The row number indicating where to start.\n",
    "        column : int\n",
    "            The column number indicating where to start.\n",
    "            \n",
    "        Returns\n",
    "        -------\n",
    "        None\n",
    "        \n",
    "        \"\"\"\n",
    "        self._send_cmd(0xB0 + row)\n",
    "        self._send_cmd(0x00 + (8*column & 0x0F))\n",
    "        self._send_cmd(0x10 + ((8*column>>4)&0x0F))\n",
    "        \n",
    "    def write(self, text):\n",
    "        \"\"\"Write the strings to the OLED screen.\n",
    "        \n",
    "        This is the method to be used when writing strings.\n",
    "        \n",
    "        Parameters\n",
    "        ----------\n",
    "        text : str\n",
    "            A string to be put onto the screen.\n",
    "            \n",
    "        Returns\n",
    "        -------\n",
    "        None\n",
    "        \n",
    "        \"\"\"\n",
    "        for i in range(len(text)):\n",
    "            self._put_char(text[i])\n",
    "    \n",
    "    def clear(self):\n",
    "        \"\"\"Clear the OLED screen.\n",
    "        \n",
    "        Parameters\n",
    "        ----------\n",
    "        None\n",
    "        \n",
    "        Returns\n",
    "        -------\n",
    "        None\n",
    "        \n",
    "        \"\"\"\n",
    "        for i in range(8):\n",
    "            self.set_XY(i,0)\n",
    "            for j in range(16):  \n",
    "                self._put_char(' ')\n",
    "        self.set_XY(0,0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Test of the PMOD Grove OLED - using above Python Class\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from pynq import PL\n",
    "from pynq.lib.pmod import PMOD_GROVE_G3\n",
    "\n",
    "# Flush IOP\n",
    "PL.reset()\n",
    "\n",
    "oled = Python_Grove_OLED(base.PMODB,PMOD_GROVE_G3)\n",
    "oled.clear()\n",
    "oled.write('Hi from Python.')\n",
    "del oled"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Test of the PMOD Grove OLED - using existing Microblaze Driver\n",
    "\n",
    "Notice how much faster the text is driven to the OLED screen.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": false
   },
   "outputs": [],
   "source": [
    "from pynq.lib.pmod import Grove_OLED\n",
    "from pynq.lib.pmod import PMOD_GROVE_G3\n",
    "\n",
    "# Flush IOP\n",
    "PL.reset()\n",
    "oled = Grove_OLED(base.PMODB,PMOD_GROVE_G3)\n",
    "oled.clear()\n",
    "oled.write('Hello from      Microblaze.')\n",
    "del oled"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.4.3+"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
